package net.ufrog.common.data;

import com.alibaba.druid.pool.DruidDataSource;
import net.ufrog.common.app.App;
import net.ufrog.common.app.PropertiesValue;
import net.ufrog.common.utils.Cryptos;
import net.ufrog.common.utils.Strings;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.sql.SQLException;

/**
 * 配置简化工具
 *
 * @author ultrafrog, ufrog.net@gmail.com
 * @version 0.1, 2017-07-07
 * @since 0.1
 */
public class SpringDataConfigurations {

    /**  */
    private SpringDataConfigurations() {}

    /**
     * @param key key for datasource name
     * @return druid datasource
     * @throws SQLException if exception then throw
     */
    public static DruidDataSource druidDataSource(String key) throws SQLException {
        DruidDataSourceProperties ddsp = App.bean("datasource" + (!Strings.empty(key) ? "." + key : ""), DruidDataSourceProperties.class);
        DruidDataSource druidDataSource = new DruidDataSource();

        druidDataSource.setUrl(ddsp.getUrl());
        druidDataSource.setUsername(ddsp.getUsername());
        druidDataSource.setPassword(Cryptos.decryptAES(ddsp.getPassword()));
        druidDataSource.setInitialSize(ddsp.getInitialSize());
        druidDataSource.setMinIdle(ddsp.getMinIdle());
        druidDataSource.setMaxActive(ddsp.getMaxActive());
        druidDataSource.setMaxWait(ddsp.getMaxWait());
        druidDataSource.setTimeBetweenEvictionRunsMillis(ddsp.getTimeBetweenEvictionRunsMillis());
        druidDataSource.setMinEvictableIdleTimeMillis(ddsp.getMinEvictableIdleTimeMillis());
        druidDataSource.setValidationQuery(ddsp.getValidationQuery());
        druidDataSource.setTestWhileIdle(ddsp.getTestWhileIdle());
        druidDataSource.setTestOnBorrow(ddsp.getTestOnBorrow());
        druidDataSource.setTestOnReturn(ddsp.getTestOnReturn());
        druidDataSource.setPoolPreparedStatements(ddsp.getPoolPreparedStatements());
        druidDataSource.setMaxPoolPreparedStatementPerConnectionSize(ddsp.getMaxPoolPreparedStatementPerConnectionSize());
        druidDataSource.setFilters(ddsp.getFilters());
        return druidDataSource;
    }

    /**
     * @param key key
     * @param dataSource data source
     * @param packagesToScan packages to scan
     * @return local container entity manager factory bean
     */
    public static LocalContainerEntityManagerFactoryBean hibernateJpaEntityManagerFactory(String key, DataSource dataSource, String... packagesToScan) {
        JpaProperties jp = App.bean("datasource" + (Strings.empty(key) ? "." + key : "") + ".jpa", JpaProperties.class);
        LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();

        hibernateJpaVendorAdapter.setShowSql(jp.getShowSql());
        hibernateJpaVendorAdapter.setDatabase(Database.valueOf(jp.getDatabase()));
        localContainerEntityManagerFactoryBean.setDataSource(dataSource);
        localContainerEntityManagerFactoryBean.setPackagesToScan(packagesToScan);
        localContainerEntityManagerFactoryBean.setJpaVendorAdapter(hibernateJpaVendorAdapter);
        return localContainerEntityManagerFactoryBean;
    }

    /**
     * @param entityManagerFactory entity manager factory
     * @return jpa transaction manager
     */
    public static JpaTransactionManager jpaTransactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
        jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
        return jpaTransactionManager;
    }

    /**
     * @author ultrafrog, ufrog.net@gmail.com
     * @version 0.1, 2017-07-07
     * @since 0.1
     */
    public static class DruidDataSourceProperties {

        @PropertiesValue("url")
        private String url;

        @PropertiesValue("username")
        private String username;

        @PropertiesValue("password")
        private String password;

        @PropertiesValue(value = "initial-size", defaultValue = "5")
        private Integer initialSize;

        @PropertiesValue(value = "min-idle", defaultValue = "5")
        private Integer minIdle;

        @PropertiesValue(value = "max-active", defaultValue = "10")
        private Integer maxActive;

        @PropertiesValue(value = "max-wait", defaultValue = "60000")
        private Long maxWait;

        @PropertiesValue(value = "time-between-eviction-runs-millis", defaultValue = "60000")
        private Long timeBetweenEvictionRunsMillis;

        @PropertiesValue(value = "min-evictable-idle-time-millis", defaultValue = "300000")
        private Long minEvictableIdleTimeMillis;

        @PropertiesValue(value = "validation-query", defaultValue = "SELECT 'x'")
        private String validationQuery;

        @PropertiesValue(value = "test-while-idle", defaultValue = "true")
        private Boolean testWhileIdle;

        @PropertiesValue(value = "test-on-borrow", defaultValue = "false")
        private Boolean testOnBorrow;

        @PropertiesValue(value = "test-on-return", defaultValue = "false")
        private Boolean testOnReturn;

        @PropertiesValue(value = "pool-prepared-statements", defaultValue = "true")
        private Boolean poolPreparedStatements;

        @PropertiesValue(value = "max-pool-prepared-statement-per-connection-size", defaultValue = "20")
        private Integer maxPoolPreparedStatementPerConnectionSize;

        @PropertiesValue(value = "filters", defaultValue = "stat")
        private String filters;

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }

        public String getPassword() {
            return password;
        }

        public void setPassword(String password) {
            this.password = password;
        }

        public Integer getInitialSize() {
            return initialSize;
        }

        public void setInitialSize(Integer initialSize) {
            this.initialSize = initialSize;
        }

        public Integer getMinIdle() {
            return minIdle;
        }

        public void setMinIdle(Integer minIdle) {
            this.minIdle = minIdle;
        }

        public Integer getMaxActive() {
            return maxActive;
        }

        public void setMaxActive(Integer maxActive) {
            this.maxActive = maxActive;
        }

        public Long getMaxWait() {
            return maxWait;
        }

        public void setMaxWait(Long maxWait) {
            this.maxWait = maxWait;
        }

        public Long getTimeBetweenEvictionRunsMillis() {
            return timeBetweenEvictionRunsMillis;
        }

        public void setTimeBetweenEvictionRunsMillis(Long timeBetweenEvictionRunsMillis) {
            this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
        }

        public Long getMinEvictableIdleTimeMillis() {
            return minEvictableIdleTimeMillis;
        }

        public void setMinEvictableIdleTimeMillis(Long minEvictableIdleTimeMillis) {
            this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
        }

        public String getValidationQuery() {
            return validationQuery;
        }

        public void setValidationQuery(String validationQuery) {
            this.validationQuery = validationQuery;
        }

        public Boolean getTestWhileIdle() {
            return testWhileIdle;
        }

        public void setTestWhileIdle(Boolean testWhileIdle) {
            this.testWhileIdle = testWhileIdle;
        }

        public Boolean getTestOnBorrow() {
            return testOnBorrow;
        }

        public void setTestOnBorrow(Boolean testOnBorrow) {
            this.testOnBorrow = testOnBorrow;
        }

        public Boolean getTestOnReturn() {
            return testOnReturn;
        }

        public void setTestOnReturn(Boolean testOnReturn) {
            this.testOnReturn = testOnReturn;
        }

        public Boolean getPoolPreparedStatements() {
            return poolPreparedStatements;
        }

        public void setPoolPreparedStatements(Boolean poolPreparedStatements) {
            this.poolPreparedStatements = poolPreparedStatements;
        }

        public Integer getMaxPoolPreparedStatementPerConnectionSize() {
            return maxPoolPreparedStatementPerConnectionSize;
        }

        public void setMaxPoolPreparedStatementPerConnectionSize(Integer maxPoolPreparedStatementPerConnectionSize) {
            this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
        }

        public String getFilters() {
            return filters;
        }

        public void setFilters(String filters) {
            this.filters = filters;
        }
    }

    /**
     * @author ultrafrog, ufrog.net@gmail.com
     * @version 0.1, 2017-07-07
     * @since 0.1
     */
    public static class JpaProperties {

        @PropertiesValue(value = "show-sql", defaultValue = "false")
        private Boolean showSql;

        @PropertiesValue(value = "database", defaultValue = "MYSQL")
        private String database;

        public Boolean getShowSql() {
            return showSql;
        }

        public void setShowSql(Boolean showSql) {
            this.showSql = showSql;
        }

        public String getDatabase() {
            return database;
        }

        public void setDatabase(String database) {
            this.database = database;
        }
    }
}
